/*
 * Copyright 2010-2012 Susanta Tewari. <freecode4susant@users.sourceforge.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package bd.org.apache.commons.math.distribution;

import bd.org.apache.commons.math.exception.NumberIsTooLargeException;
import bd.org.apache.commons.math.exception.util.LocalizedFormats;

/**
 * Implementation of the uniform real distribution.
 *
 * @version $Id: UniformRealDistribution.java 1244107 2012-02-14 16:17:55Z erans $
 * @see <a href="http://en.wikipedia.org/wiki/Uniform_distribution_(continuous)"
 *      >Uniform distribution (continuous), at Wikipedia</a>
 * @since 3.0
 */
public class UniformRealDistribution extends AbstractRealDistribution {

    /**
     * Default inverse cumulative probability accuracy.
     */
    public static final double DEFAULT_INVERSE_ABSOLUTE_ACCURACY = 1e-9;

    /**
     * Serializable version identifier.
     */
    private static final long serialVersionUID = 20120109L;

    /**
     * Lower bound of this distribution (inclusive).
     */
    private final double lower;

    /**
     * Upper bound of this distribution (exclusive).
     */
    private final double upper;

    /**
     * Inverse cumulative probability accuracy.
     */
    private final double solverAbsoluteAccuracy;

    /**
     * Create a standard uniform real distribution with lower bound (inclusive)
     * equal to zero and upper bound (exclusive) equal to one.
     */
    public UniformRealDistribution() {

        this(0, 1);
    }

    /**
     * Create a uniform real distribution using the given lower and upper
     * bounds.
     *
     * @param lower Lower bound of this distribution (inclusive).
     * @param upper Upper bound of this distribution (exclusive).
     * @throws NumberIsTooLargeException if {@code lower >= upper}.
     */
    public UniformRealDistribution(double lower, double upper) throws NumberIsTooLargeException {

        this(lower, upper, DEFAULT_INVERSE_ABSOLUTE_ACCURACY);
    }

    /**
     * Create a normal distribution using the given mean, standard deviation and
     * inverse cumulative distribution accuracy.
     *
     * @param lower Lower bound of this distribution (inclusive).
     * @param upper Upper bound of this distribution (exclusive).
     * @param inverseCumAccuracy Inverse cumulative probability accuracy.
     * @throws NumberIsTooLargeException if {@code lower >= upper}.
     */
    public UniformRealDistribution(double lower, double upper, double inverseCumAccuracy)
            throws NumberIsTooLargeException {

        if (lower >= upper) {

            throw new NumberIsTooLargeException(LocalizedFormats.LOWER_BOUND_NOT_BELOW_UPPER_BOUND,
                    lower, upper, false);
        }

        this.lower             = lower;
        this.upper             = upper;
        solverAbsoluteAccuracy = inverseCumAccuracy;
    }

    /**
     * {@inheritDoc}
     * For this distribution {@code P(X = x)} always evaluates to 0.
     *
     * @return 0
     */
    @Override
    public double probability(double x) {

        return 0.0;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public double density(double x) {

        if ((x < lower) || (x > upper)) {
            return 0.0;
        }

        return 1 / (upper - lower);
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public double cumulativeProbability(double x) {

        if (x <= lower) {
            return 0;
        }

        if (x >= upper) {
            return 1;
        }

        return (x - lower) / (upper - lower);
    }


    /**
     * {@inheritDoc}
     */
    @Override
    protected double getSolverAbsoluteAccuracy() {

        return solverAbsoluteAccuracy;
    }


    /**
     * {@inheritDoc}
     * For lower bound {@code lower} and upper bound {@code upper}, the mean is
     * {@code 0.5 * (lower + upper)}.
     */
    @Override
    public double getNumericalMean() {

        return 0.5 * (lower + upper);
    }


    /**
     * {@inheritDoc}
     * For lower bound {@code lower} and upper bound {@code upper}, the
     * variance is {@code (upper - lower)^2 / 12}.
     */
    @Override
    public double getNumericalVariance() {

        double ul = upper - lower;

        return ul * ul / 12;
    }


    /**
     * {@inheritDoc}
     * The lower bound of the support is equal to the lower bound parameter
     * of the distribution.
     *
     * @return lower bound of the support
     */
    @Override
    public double getSupportLowerBound() {

        return lower;
    }


    /**
     * {@inheritDoc}
     * The upper bound of the support is equal to the upper bound parameter
     * of the distribution.
     *
     * @return upper bound of the support
     */
    @Override
    public double getSupportUpperBound() {

        return upper;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSupportLowerBoundInclusive() {

        return true;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isSupportUpperBoundInclusive() {

        return false;
    }


    /**
     * {@inheritDoc}
     * The support of this distribution is connected.
     *
     * @return {@code true}
     */
    @Override
    public boolean isSupportConnected() {

        return true;
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public double sample() {

        return randomData.nextUniform(lower, upper, true);
    }
}
